package com.nulldev.util.languages.json.uniapi;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collector;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.languages.json.lazy.LazyArray;

class FastJSONStream implements Stream<Object> {

	private final LazyArray lza;
	private final Stream<Integer> obj;
	private final UniJsonValue ref;
	private final boolean parallel;

	public FastJSONStream(final UniJsonValue ref, final Object o, final boolean parallel) {
		this.lza = (LazyArray) o;
		if (parallel) {
			this.obj = Stream.iterate(0, n -> n + 1).limit(this.lza.length()).parallel();
		} else {
			this.obj = Stream.iterate(0, n -> n + 1).limit(this.lza.length());
		}
		this.ref = ref;
		this.parallel = parallel;
	}

	private FastJSONStream(final Stream<Integer> obj, final LazyArray lza, final UniJsonValue ref, final boolean parallel) {
		this.obj = obj;
		this.lza = lza;
		this.ref = ref;
		this.parallel = parallel;
	}

	@Override
	public Iterator<Object> iterator() {
		final Iterator<UniJsonValue> o = this.ref.raw_array();
		return new Iterator<Object>() {

			@Override
			public boolean hasNext() {
				return o.hasNext();
			}

			@Override
			public Object next() {
				return o.next();
			}
		};
	}

	@Override
	public Spliterator<Object> spliterator() {
		final Iterator<UniJsonValue> v = this.ref.raw_array();
		return Spliterators.spliterator(v, this.ref.size(), 0);
	}

	@Override
	public boolean isParallel() {
		return this.parallel && this.obj.isParallel();
	}

	@Override
	public Stream<Object> sequential() {
		return this;
	}

	@Override
	public Stream<Object> parallel() {
		return this;
	}

	@Override
	public Stream<Object> unordered() {
		return this;
	}

	@Override
	public Stream<Object> onClose(final Runnable closeHandler) {
		obj.onClose(closeHandler);
		return this;
	}

	@Override
	public void close() {
		obj.close();
	}

	@Override
	public Stream<Object> filter(final Predicate<? super Object> predicate) {
		return new FastJSONStream(this.obj.filter(new Predicate<Integer>() {

			@Override
			public boolean test(final Integer t) {
				return predicate.test(FastJSONStream.this.lza.get(t));
			}
		}), this.lza, this.ref, this.parallel);
	}

	@Override
	public <R> Stream<R> map(final Function<? super Object, ? extends R> mapper) {
		return null;
	}

	@Override
	public IntStream mapToInt(final ToIntFunction<? super Object> mapper) {
		return null;
	}

	@Override
	public LongStream mapToLong(final ToLongFunction<? super Object> mapper) {
		return null;
	}

	@Override
	public DoubleStream mapToDouble(final ToDoubleFunction<? super Object> mapper) {
		return null;
	}

	@Override
	public <R> Stream<R> flatMap(final Function<? super Object, ? extends Stream<? extends R>> mapper) {
		return null;
	}

	@Override
	public IntStream flatMapToInt(final Function<? super Object, ? extends IntStream> mapper) {
		return null;
	}

	@Override
	public LongStream flatMapToLong(final Function<? super Object, ? extends LongStream> mapper) {
		return null;
	}

	@Override
	public DoubleStream flatMapToDouble(final Function<? super Object, ? extends DoubleStream> mapper) {
		return null;
	}

	@Override
	public Stream<Object> distinct() {
		return this;
	}

	@Override
	public Stream<Object> sorted() {
		return this;
	}

	@Override
	public Stream<Object> sorted(final Comparator<? super Object> comparator) {
		return this;
	}

	@Override
	public Stream<Object> peek(final Consumer<? super Object> action) {
		obj.forEach(new Consumer<Integer>() {

			@Override
			public void accept(final Integer t) {
				action.accept(new UniJsonValue(lza.get(t)));
			}
		});
		return this;
	}

	@Override
	public Stream<Object> limit(final long maxSize) {
		return this;
	}

	@Override
	public Stream<Object> skip(final long n) {
		return this;
	}

	@Override
	public void forEach(final Consumer<? super Object> action) {
		obj.forEach(new Consumer<Integer>() {

			@Override
			public void accept(final Integer t) {
				action.accept(new UniJsonValue(lza.get(t)));
			}
		});
	}

	@Override
	public void forEachOrdered(Consumer<? super Object> action) {
		obj.forEachOrdered(new Consumer<Integer>() {

			@Override
			public void accept(final Integer t) {
				action.accept(new UniJsonValue(lza.get(t)));
			}
		});
	}

	@Override
	public Object[] toArray() {
		return obj.toArray();
	}

	@Override
	public <A> A[] toArray(final IntFunction<A[]> generator) {
		return obj.toArray(generator);
	}

	@Override
	public UniJsonValue reduce(final Object identity, final BinaryOperator<Object> accumulator) {
		return null;
	}

	@Override
	public Optional<Object> reduce(final BinaryOperator<Object> accumulator) {
		return Optional.empty();
	}

	@Override
	public <U> U reduce(final U identity, final BiFunction<U, ? super Object, U> accumulator, final BinaryOperator<U> combiner) {
		return null;
	}

	@Override
	public <R> R collect(final Supplier<R> supplier, final BiConsumer<R, ? super Object> accumulator, final BiConsumer<R, R> combiner) {
		return null;
	}

	@Override
	public <R, A> R collect(final Collector<? super Object, A, R> collector) {
		return null;
	}

	@Override
	public Optional<Object> min(final Comparator<? super Object> comparator) {
		return Optional.empty();
	}

	@Override
	public Optional<Object> max(final Comparator<? super Object> comparator) {
		return Optional.empty();
	}

	@Override
	public long count() {
		return obj.count();
	}

	@Override
	public boolean anyMatch(final Predicate<? super Object> predicate) {
		return false;
	}

	@Override
	public boolean allMatch(final Predicate<? super Object> predicate) {
		return false;
	}

	@Override
	public boolean noneMatch(final Predicate<? super Object> predicate) {
		return false;
	}

	@Override
	public Optional<Object> findFirst() {
		final Integer idx = this.obj.findFirst().orElse(null);
		if (idx == null)
			return Optional.empty();
		return Optional.of(this.lza.get(idx));
	}

	@Override
	public Optional<Object> findAny() {
		final Integer idx = this.obj.findAny().orElse(null);
		if (idx == null)
			return Optional.empty();
		return Optional.of(this.lza.get(idx));
	}

	public static void main(String[] args) {
		Arguments.setup(new String[]
			{ "--JSONAPI.fast", "--UniJsonValue.api=fast" });
		final UniJsonValue arr = UniJsonValue.array();
		arr.print_providers();
		JVM.println("=============");
		arr.add(UniJsonValue.object());
		arr.add("Hello world!");
		arr.add("4");
		arr.stream(true).forEach((f) -> {
			JVM.println("f: " + f);
		});
		arr.stream().filter(e -> e instanceof String && ((String) e).equals("4")).forEach(JVM::println);
		JVM.println(arr.stream().findAny());
	}
}
